﻿using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

using Umbraco.Cms.Web.Model.BackOffice.PropertyEditors;
using System.Web.Mvc;
using Umbraco.Cms.Web.EmbeddedViewEngine;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using Umbraco.Cms.Web.Context;
using Umbraco.Framework;
using Umbraco.Hive.ProviderGrouping;
using Umbraco.Hive.RepositoryTypes;
using File = Umbraco.Framework.Persistence.Model.IO.File;
using System.ComponentModel;
using Umbraco.Hive;
using System.Web;
using System.Web.Routing;
using System.IO;
using Umbraco.Framework.Persistence.Model.Constants;
using Umbraco.Framework.Persistence.Model.Associations;

namespace idseefeld.com.imagecropper
{
	/// <summary>
	/// Image Cropper for Umbraco 5 - released under MIT License
	/// Copyright (c) 2012 Dirk Seefeld
	/// http://imagecropperumbraco5.codeplex.com/
	/// </summary>
	[EmbeddedView("idseefeld.com.imagecropper.Views.ImageCropperV5.cshtml", "idseefeld.com.imagecropper")]
	[Bind(Exclude = "ContentId,PropertyAlias,File")]
	public class ImageCropperV5Model : EditorModel<ImageCropperV5PreValueModel>
	{
		#region private Properties
		private readonly IBackOfficeRequestContext _backOfficeRequestContext;
		private readonly GroupUnitFactory<IFileStore> _hive;
		private readonly HiveId _contentId;
		private readonly string _propertyAlias;
		private readonly int _reducedWidthHeight;
		private readonly int _previewWidth;
		private readonly int _previewHeight;
		private readonly string _aspectRatio;
		private readonly string _cropName;
		private readonly string _position;
		private readonly string _imageMediaPropertyAlias;
		private readonly string _cropImageSrc;
		private readonly string _imageWidth;
		private readonly string _imageHeight;
		private readonly string _previewStyle;
		private readonly string _javascriptDebug;
		private readonly int _quality;

		private string _originalImageUrl;
		private string _imgUrl;

		private File _originalImageFile;

		#endregion

		#region Properties
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public HiveId ContentId { get { return _contentId; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public int ReducedWidthHeight { get { return _reducedWidthHeight; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string PropertyAlias { get { return _propertyAlias; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public int PreviewWidth { get { return _previewWidth; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public int PreviewHeight { get { return _previewHeight; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string AspectRatio { get { return _aspectRatio; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string CropName { get { return _cropName; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string Position { get { return _position; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string ImageMediaPropertyAlias { get { return _imageMediaPropertyAlias; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string CropImageSrc { get { return _cropImageSrc; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string ImageWidth { get { return _imageWidth; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string ImageHeight { get { return _imageHeight; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string PreviewStyle { get { return _previewStyle; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public string JavascriptDebug { get { return _javascriptDebug; } }
		[ScaffoldColumn(false)]
		[ReadOnly(true)]
		public int Quality { get { return _quality; } }

		//[Range(0, 255, ErrorMessage = "Please select the Red value of 0 - 255")]
		[HiddenInput]
		//[DisplayName("Crop width")]
		public int CropWidth { get; set; }
		[HiddenInput]
		public int CropHeight { get; set; }
		[HiddenInput]
		public int CropX { get; set; }
		[HiddenInput]
		public int CropY { get; set; }

		public string CropperValues { get; set; }
		#endregion

		public override bool ShowUmbracoLabel
		{
			get
			{
				return PreValueModel.showLabel;
			}
		}

		/// <summary>
		/// Image Cropper Model
		/// based on UploadEdtior ns: Umbraco.Cms.Web.PropertyEditors.Upload
		/// </summary>
		/// <param name="preValues"></param>
		/// <param name="backOfficeRequestContext"></param>
		/// <param name="contentId"></param>
		/// <param name="propertyAlias"></param>
		public ImageCropperV5Model(ImageCropperV5PreValueModel preValues, IBackOfficeRequestContext backOfficeRequestContext,
			HiveId contentId, string propertyAlias)
			: base(preValues)
		{
			_backOfficeRequestContext = backOfficeRequestContext;
			_hive = _backOfficeRequestContext.Application.Hive.GetWriter<IFileStore>(new Uri("storage://file-uploader"));
			_contentId = contentId;
			_propertyAlias = propertyAlias;

			_reducedWidthHeight = preValues.reducedWidthHeight;

			CropX = 0;
			CropY = 0;

			_previewWidth = preValues.cropWidth;
			_previewHeight = preValues.cropHeight;
			_previewStyle = preValues.showPreview ? "" : "display:none";

			_aspectRatio = preValues.aspectRatio.ToString().ToLower();
			_position = preValues.position;
			_cropName = preValues.cropName;
			_quality = preValues.quality;

			//get the image url from hive by property alias
			_imageMediaPropertyAlias = preValues.imagePropertyAlias;
			if (contentId.IsNullValueOrEmpty())
				return;

			var entity = UmbracoHelper.GetEntiyByHiveId(contentId);
			if (entity == null)
				return;

			var appContext = DependencyResolver.Current.GetService<IUmbracoApplicationContext>();
			using (var securityUow = appContext.Hive.OpenReader<ISecurityStore>()) {
				// Check to see if anonymous view is allowed
				var anonymousViewAllowed = !appContext.Security.PublicAccess.IsProtected(entity.Id);

				// Get upload property
				var prop = entity.Attributes.SingleOrDefault(x => x.AttributeDefinition.Alias == _imageMediaPropertyAlias);

				if (!(prop == null || !prop.Values.ContainsKey("MediaId"))) {
					var mediaId = prop.Values["MediaId"].ToString();
					var fileId = new HiveId(prop.Values["Value"].ToString());
					if (!fileId.IsNullValueOrEmpty()) {
						using (var fileUow = appContext.Hive.OpenReader<IFileStore>(fileId.ToUri())) {
							_originalImageFile = fileUow.Repositories.Get<File>(fileId);
						}
					}
				}
			}
			_imgUrl = null;
			try {
				HttpContextBase contextBase = new HttpContextWrapper(HttpContext.Current);
				RequestContext reqContext = new RequestContext(contextBase, RouteTable.Routes.GetRouteData(contextBase));
				string applictionPath = HttpContext.Current.Request.ApplicationPath;
				_imgUrl = UmbracoMediaHelper.GetMediaUrl(ContentId, ImageMediaPropertyAlias, ReducedWidthHeight);
				if (!String.IsNullOrEmpty(_imgUrl)) {
					_imgUrl = VirtualPathUtility.ToAbsolute(_imgUrl, applictionPath);
				}
			}
			catch { }

			if (!String.IsNullOrEmpty(_imgUrl)) {
				_cropImageSrc = _imgUrl;
				var img = Image.FromFile(HttpContext.Current.Server.MapPath(_imgUrl));
				_imageWidth = img.Width.ToString();
				_imageHeight = img.Height.ToString();

			}

			#region js debug
			//start frontend debug
			//can be removed finally
#if (DEBUG)
			_javascriptDebug = "true";
#else
			_javascriptDebug = "false";
#endif
			//end debug
			#endregion
		}


		private string GetCropImageSrcName(string sourceAlias, string name)
		{
			string result = name;
			return result;
		}
		public override IDictionary<string, object> GetSerializedValue()
		{
			if (CropWidth > 0 && !String.IsNullOrEmpty(CropImageSrc)) {
				CropperValues = string.Format("{0},{1},{2},{3},{4}", CropX, CropY, CropWidth, CropHeight, CropImageSrc);
				using (var uow = _hive.Create()) {
					string cropFileName;// = string.Format("{0}_{1}_{2}_{3}_{4}", CropX, CropY, CropWidth, CropHeight, CropName);
					cropFileName = CropName;

					Image originalImage = Image.FromFile(HttpContext.Current.Server.MapPath(_imgUrl));

					var cropRect = new Rectangle(CropX, CropY, CropWidth, CropHeight);

					#region code to be improved?
					//I dont think this is the propper way to retrieve the mediaId of an uploaded image
					string imgPath = HttpContext.Current.Server.MapPath(_imgUrl);
					string fileExtension = Path.GetExtension(_originalImageFile.Name).ToLower();
					int lastPosOfSlash = _imgUrl.LastIndexOf('/');
					string cropImgaeMediaId = "";
					if (lastPosOfSlash > 0) {
						string cropRootPath = _imgUrl.Substring(0, lastPosOfSlash);
						lastPosOfSlash = cropRootPath.LastIndexOf('/');
						cropImgaeMediaId = cropRootPath.Substring(lastPosOfSlash + 1);
					}
					#endregion

					CreateCropImage(uow, _originalImageFile, originalImage, cropImgaeMediaId, cropFileName, cropRect, PreviewWidth, PreviewHeight, bool.Parse(AspectRatio));

				}
			}

			return base.GetSerializedValue();
		}

		//based on umbraco function of ns: Umbraco.Cms.Web.PropertyEditors.Upload CreateThumbnail 
		private void CreateCropImage(IGroupUnit<IFileStore> uow, File original, Image image, string mediaId, string fileName, Rectangle cropRect, int destWidth, int destHeight, bool keepAspect)
		{
			var extension = Path.GetExtension(original.Name).ToLower();
			var cropFileName = Path.GetFileNameWithoutExtension(original.Name) + "_" + fileName + extension;

			// Create file entity
			var cropImageFile = new File
			{
				RootedPath = mediaId + "/" + cropFileName
			};
			double factorX = (double)destWidth / (double)cropRect.Width;
			double factorY = (double)destHeight / (double)cropRect.Height;
			if (PreviewWidth == 0) {
				factorX = factorY;
				destWidth = (int)Math.Round(PreviewHeight * (double)cropRect.Width / (double)cropRect.Height);
			}
			else if (PreviewHeight == 0) {
				factorY = factorX;
				destHeight = (int)Math.Round(PreviewWidth * (double)cropRect.Height / (double)cropRect.Width);
			}
			else if (keepAspect) {
				factorY = factorX;
			}

			using (var bitmap = new Bitmap(destWidth, destHeight)) {
				using (var graphics = Graphics.FromImage(bitmap)) {
					graphics.SmoothingMode = SmoothingMode.HighQuality;
					graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
					graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

					graphics.DrawImage(image,
						new Rectangle(0, 0, destWidth, destHeight),
						cropRect.X, cropRect.Y, cropRect.Width, cropRect.Height,
						GraphicsUnit.Pixel);

					var imageEncoders = ImageCodecInfo.GetImageEncoders();
					ImageCodecInfo encoder = null;
					if (extension == ".png" || extension == ".gif")
						encoder = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
					else
						encoder = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));

					var stream = new MemoryStream();
					var encoderParameters = new EncoderParameters();
					long compressionQuality = (long)_quality;
					encoderParameters.Param[0] = new EncoderParameter(global::System.Drawing.Imaging.Encoder.Quality, compressionQuality);
					bitmap.Save(stream, encoder, encoderParameters);

					cropImageFile.ContentBytes = stream.ToArray();
				}
			}

			// Add or update file
			uow.Repositories.AddOrUpdate(cropImageFile);

			// Create relation
			uow.Repositories.AddRelation(original, cropImageFile, FixedRelationTypes.ThumbnailRelationType, 0, new RelationMetaDatum("size", CropName));
		}


	}
}
